//
// detail/executor_function.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2022 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#ifndef ASIO_DETAIL_EXECUTOR_FUNCTION_HPP
#define ASIO_DETAIL_EXECUTOR_FUNCTION_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include "asio/detail/config.hpp"
#include "asio/detail/handler_alloc_helpers.hpp"
#include "asio/detail/handler_invoke_helpers.hpp"
#include "asio/detail/memory.hpp"

#include "asio/detail/push_options.hpp"

namespace asio {
    namespace detail {

#if defined(ASIO_HAS_MOVE)

        // Lightweight, move-only function object wrapper.
        class executor_function
        {
        public:
          template <typename F, typename Alloc>
          explicit executor_function(F f, const Alloc& a)
          {
            // Allocate and construct an object to wrap the function.
            typedef impl<F, Alloc> impl_type;
            typename impl_type::ptr p = {
              detail::addressof(a), impl_type::ptr::allocate(a), 0 };
            impl_ = new (p.v) impl_type(ASIO_MOVE_CAST(F)(f), a);
            p.v = 0;
          }

          executor_function(executor_function&& other) ASIO_NOEXCEPT
            : impl_(other.impl_)
          {
            other.impl_ = 0;
          }

          ~executor_function()
          {
            if (impl_)
              impl_->complete_(impl_, false);
          }

          void operator()()
          {
            if (impl_)
            {
              impl_base* i = impl_;
              impl_ = 0;
              i->complete_(i, true);
            }
          }

        private:
          // Base class for polymorphic function implementations.
          struct impl_base
          {
            void (*complete_)(impl_base*, bool);
          };

          // Polymorphic function implementation.
          template <typename Function, typename Alloc>
          struct impl : impl_base
          {
            ASIO_DEFINE_TAGGED_HANDLER_ALLOCATOR_PTR(
                thread_info_base::executor_function_tag, impl);

            template <typename F>
            impl(ASIO_MOVE_ARG(F) f, const Alloc& a)
              : function_(ASIO_MOVE_CAST(F)(f)),
                allocator_(a)
            {
              complete_ = &executor_function::complete<Function, Alloc>;
            }

            Function function_;
            Alloc allocator_;
          };

          // Helper to complete function invocation.
          template <typename Function, typename Alloc>
          static void complete(impl_base* base, bool call)
          {
            // Take ownership of the function object.
            impl<Function, Alloc>* i(static_cast<impl<Function, Alloc>*>(base));
            Alloc allocator(i->allocator_);
            typename impl<Function, Alloc>::ptr p = {
              detail::addressof(allocator), i, i };

            // Make a copy of the function so that the memory can be deallocated before
            // the upcall is made. Even if we're not about to make an upcall, a
            // sub-object of the function may be the true owner of the memory
            // associated with the function. Consequently, a local copy of the function
            // is required to ensure that any owning sub-object remains valid until
            // after we have deallocated the memory here.
            Function function(ASIO_MOVE_CAST(Function)(i->function_));
            p.reset();

            // Make the upcall if required.
            if (call)
            {
              asio_handler_invoke_helpers::invoke(function, function);
            }
          }

          impl_base* impl_;
        };

#else // defined(ASIO_HAS_MOVE)

// Not so lightweight, copyable function object wrapper.
        class executor_function {
        public:
            template<typename F, typename Alloc>
            explicit executor_function(const F &f, const Alloc &)
                    : impl_(new impl<typename decay<F>::type>(f)) {
            }

            void operator()() {
                impl_->complete_(impl_.get());
            }

        private:
            // Base class for polymorphic function implementations.
            struct impl_base {
                void (*complete_)(impl_base *);
            };

            // Polymorphic function implementation.
            template<typename F>
            struct impl : impl_base {
                impl(const F &f)
                        : function_(f) {
                    complete_ = &executor_function::complete<F>;
                }

                F function_;
            };

            // Helper to complete function invocation.
            template<typename F>
            static void complete(impl_base *i) {
                static_cast<impl<F> *>(i)->function_();
            }

            shared_ptr <impl_base> impl_;
        };

#endif // defined(ASIO_HAS_MOVE)

// Lightweight, non-owning, copyable function object wrapper.
        class executor_function_view {
        public:
            template<typename F>
            explicit executor_function_view(F &f)

            ASIO_NOEXCEPT
                    : complete_(&executor_function_view::complete<F>),
                    function_( &
            f)
            {
            }

            void operator()() {
                complete_(function_);
            }

        private:
            // Helper to complete function invocation.
            template<typename F>
            static void complete(void *f) {
                (*static_cast<F *>(f))();
            }

            void (*complete_)(void *);

            void *function_;
        };

    } // namespace detail
} // namespace asio

#include "asio/detail/pop_options.hpp"

#endif // ASIO_DETAIL_EXECUTOR_FUNCTION_HPP
